/*
 * Description: 性能相关配置文件读取工具类
 */
#include "perf_config_factory.h"
#include "common/guest_utils.h"
#include "log.h"
#include "performance/perf_constants.h"
#include <Windows.h>

namespace PERF {
std::shared_ptr<PerfConfig> PerfConfigFactory::GetConfig()
{
    if (config == nullptr) {
        std::unique_lock<std::mutex> lock(perfConfigMutex);
        if (config == nullptr) {
            config = ParseConfig(ReadConfigStrFromFile());
        }
    }
    return config;
}

void PerfConfigFactory::ClearConfig()
{
    if (config) {
        config.reset();
    }
}

std::string PerfConfigFactory::ReadConfigStrFromFile()
{
    LOG_INFO("[PERF - ReadConfigStrFromFile] - begin.");
    HANDLE hConfigFile = CreateFile(PERF_CONFIG_FILE_PATH.c_str(), GENERIC_READ, 0, nullptr, OPEN_EXISTING,
                                    FILE_ATTRIBUTE_NORMAL, nullptr);
    if (hConfigFile == INVALID_HANDLE_VALUE) {
        LOG_ERR("[PERF - ReadConfigStrFromFile] - error occurred, file handle is nullptr, error code: {}",
                GetLastError());
        return "{}";
    }
    DWORD size = GetFileSize(hConfigFile, nullptr);
    DWORD sizeReaded = 0;
    char *data = new (std::nothrow) char[size + 1];
    if (data == nullptr) {
        LOG_ERR("[PERF - ReadConfigStrFromFile] - error occurred, allocate memory failed!");
        SAFE_CLOSE_HANDLE(hConfigFile, INVALID_HANDLE_VALUE);
        return "{}";
    }
    SecureZeroMemory(data, size + 1);
    if (!ReadFile(hConfigFile, data, size, &sizeReaded, nullptr) || sizeReaded != size) {
        LOG_ERR("[PERF - ReadConfigStrFromFile] - error occurred, reading file failed, error code: {}", GetLastError());
        SAFE_CLOSE_HANDLE(hConfigFile, INVALID_HANDLE_VALUE);
        delete[] data;
        return "{}";
    }
    SAFE_CLOSE_HANDLE(hConfigFile, INVALID_HANDLE_VALUE);
    std::string config = data;
    delete[] data;
    return config;
}

std::shared_ptr<PerfConfig> PerfConfigFactory::ParseConfig(const std::string &json)
{
    LOG_INFO("[PERF - ParseConfig] - begin.");
    cJSON *root = cJSON_Parse(json.c_str());
    if (!root) {
        LOG_ERR("[PERF - ParseConfig] - root is nullptr");
        return nullptr;
    }
    auto config = std::make_shared<PerfConfig>();
    if (!config) {
        LOG_ERR("[PERF - ParseConfig] - config is nullptr");
        cJSON_Delete(root);
        return nullptr;
    }
    ReadAllowedSites(root, config);
    ReadAutoruns(root, config);
    cJSON_Delete(root);
    return config;
}

bool PerfConfigFactory::ReadAllowedSites(cJSON *root, std::shared_ptr<PerfConfig> &config)
{
    cJSON *siteJsonArr = cJSON_GetObjectItem(root, "allowedSites");
    if (!siteJsonArr || siteJsonArr->type != cJSON_Array) {
        LOG_ERR("[PERF - ReadAllowedSites] - allowedSites json node not found");
        return false;
    }
    cJSON *siteJsonObject = siteJsonArr->child;
    while (siteJsonObject && siteJsonObject->type == cJSON_Object) {
        AllowedSite site;
        ReadSite(siteJsonObject, site);
        config->allowedSites.emplace_back(site);
        siteJsonObject = siteJsonObject->next;
    }
    return true;
}

bool PerfConfigFactory::ReadSite(cJSON *siteJsonObject, AllowedSite &site)
{
    if (!siteJsonObject || siteJsonObject->type != cJSON_Object) {
        LOG_ERR("[PERF - ReadSite] - allowedSites json node not found");
        return false;
    }
    cJSON *domainJsonStr = cJSON_GetObjectItem(siteJsonObject, "domain");
    if (!domainJsonStr || domainJsonStr->type != cJSON_String) {
        LOG_ERR("[PERF - ReadSite] - domain json node not found");
        return false;
    }
    site.domain = GetJsonStr(domainJsonStr);
    cJSON *protocolsJsonArr = cJSON_GetObjectItem(siteJsonObject, "protocols");
    if (!protocolsJsonArr || protocolsJsonArr->type != cJSON_Array) {
        LOG_ERR("[PERF - ReadSite] - protocols json node not found");
        return false;
    }
    cJSON *protocolJsonStr = protocolsJsonArr->child;
    while (protocolJsonStr && protocolJsonStr->type == cJSON_String) {
        site.protocols.emplace_back(GetJsonStr(protocolJsonStr));
        protocolJsonStr = protocolJsonStr->next;
    }
    return true;
}

bool PerfConfigFactory::ReadAutoruns(cJSON *root, std::shared_ptr<PerfConfig> &config)
{
    cJSON *autorunJsonArr = cJSON_GetObjectItem(root, "autoruns");
    if (!autorunJsonArr || autorunJsonArr->type != cJSON_Array) {
        LOG_ERR("[PERF - ReadAutoruns] - autoruns json node not found");
        return false;
    }
    cJSON *autorunObject = autorunJsonArr->child;
    while (autorunObject && autorunObject->type == cJSON_Object) {
        Autorun autorun;
        ReadAutorun(autorunObject, autorun);
        config->moduleNameToAutoruns.emplace(autorun.moduleName, autorun);
        autorunObject = autorunObject->next;
    }
    return true;
}

bool PerfConfigFactory::ReadAutorun(cJSON *autorunObject, Autorun &autorun)
{
    if (!autorunObject || autorunObject->type != cJSON_Object) {
        LOG_ERR("[PERF - ReadAutorun] - autorun json node not found");
        return false;
    }
    cJSON *appNameJsonStr = cJSON_GetObjectItem(autorunObject, "appName");
    if (!appNameJsonStr || appNameJsonStr->type != cJSON_String) {
        LOG_ERR("[PERF - ReadAutorun] - appName json node not found");
        return false;
    }
    autorun.appName = GetJsonStr(appNameJsonStr);
    cJSON *moduleNameJsonStr = cJSON_GetObjectItem(autorunObject, "moduleName");
    if (!moduleNameJsonStr || moduleNameJsonStr->type != cJSON_String) {
        LOG_ERR("[PERF - ReadAutorun] - moduleName json node not found");
        return false;
    }
    autorun.moduleName = GetJsonStr(moduleNameJsonStr);
    cJSON *shouldRemoveJsonBool = cJSON_GetObjectItem(autorunObject, "shouldRemove");
    if (!shouldRemoveJsonBool) {
        LOG_ERR("[PERF - ReadAutorun] - shouldRemove json node not found");
        return false;
    }
    autorun.shouldRemove = shouldRemoveJsonBool->type == cJSON_True;
    ReadRegistry(cJSON_GetObjectItem(autorunObject, "registry"), autorun);
    ReadPaths(cJSON_GetObjectItem(autorunObject, "paths"), autorun);
    ReadServices(cJSON_GetObjectItem(autorunObject, "services"), autorun);
    ReadScheduledTasks(cJSON_GetObjectItem(autorunObject, "scheduledTasks"), autorun);
    return true;
}

bool PerfConfigFactory::ReadRegistry(cJSON *registryObject, Autorun &autorun)
{
    if (!registryObject || registryObject->type != cJSON_Object) {
        return false;
    }
    std::vector<std::string> keys = { "HKCU_KEYS", "HKLM_KEYS" };
    for (std::string &key : keys) {
        cJSON *keysJsonArr = cJSON_GetObjectItem(registryObject, key.c_str());
        if (!keysJsonArr || keysJsonArr->type != cJSON_Array) {
            continue;
        }
        cJSON *keyJsonObject = keysJsonArr->child;
        while (keyJsonObject && keyJsonObject->type == cJSON_Object) {
            cJSON *keyJsonStr = cJSON_GetObjectItem(keyJsonObject, "key");
            if (!keyJsonStr || keyJsonStr->type != cJSON_String) {
                LOG_ERR("[PERF - ReadAutorun] - key json node not found");
                return false;
            }
            cJSON *itemJsonStr = cJSON_GetObjectItem(keyJsonObject, "item");
            if (!itemJsonStr || itemJsonStr->type != cJSON_String) {
                LOG_ERR("[PERF - ReadAutorun] - item json node not found");
                return false;
            }
            Registry registry;
            registry.key = GetJsonStr(keyJsonStr);
            registry.item = GetJsonStr(itemJsonStr);
            autorun.registry[key].emplace_back(registry);
            keyJsonObject = keyJsonObject->next;
        }
    }
    return true;
}

bool PerfConfigFactory::ReadPaths(cJSON *pathsJsonArr, Autorun &autorun)
{
    if (!pathsJsonArr || pathsJsonArr->type != cJSON_Array) {
        return false;
    }
    cJSON *pathJsonStr = pathsJsonArr->child;
    while (pathJsonStr && pathJsonStr->type == cJSON_String) {
        autorun.paths.emplace_back(GetJsonStr(pathJsonStr));
        pathJsonStr = pathJsonStr->next;
    }
    return true;
}

bool PerfConfigFactory::ReadServices(cJSON *servicesJsonArr, Autorun &autorun)
{
    if (!servicesJsonArr || servicesJsonArr->type != cJSON_Array) {
        return false;
    }
    cJSON *serviceJsonObject = servicesJsonArr->child;
    while (serviceJsonObject && serviceJsonObject->type == cJSON_Object) {
        cJSON *keyJsonStr = cJSON_GetObjectItem(serviceJsonObject, "key");
        if (!keyJsonStr || keyJsonStr->type != cJSON_String) {
            LOG_ERR("[PERF - ReadAutorun] - key json node not found");
            return false;
        }
        cJSON *itemJsonStr = cJSON_GetObjectItem(serviceJsonObject, "item");
        if (!itemJsonStr || itemJsonStr->type != cJSON_String) {
            LOG_ERR("[PERF - ReadAutorun] - item json node not found");
            return false;
        }
        Registry registry;
        registry.key = GetJsonStr(keyJsonStr);
        registry.item = GetJsonStr(itemJsonStr);
        autorun.services.emplace_back(registry);
        serviceJsonObject = serviceJsonObject->next;
    }
    return true;
}

bool PerfConfigFactory::ReadScheduledTasks(cJSON *scheduledTasksJsonArr, Autorun &autorun)
{
    if (!scheduledTasksJsonArr || scheduledTasksJsonArr->type != cJSON_Array) {
        return false;
    }
    cJSON *scheduledTasksJsonStr = scheduledTasksJsonArr->child;
    while (scheduledTasksJsonStr && scheduledTasksJsonStr->type == cJSON_String) {
        autorun.scheduledTasks.emplace_back(GetJsonStr(scheduledTasksJsonStr));
        scheduledTasksJsonStr = scheduledTasksJsonStr->next;
    }
    return true;
}

std::string PerfConfigFactory::GetJsonStr(cJSON *jsonStr)
{
    if (!jsonStr || jsonStr->type != cJSON_String || !jsonStr->valuestring) {
        return "";
    }
    auto entry = PERF_CFG_STR_TO_CHS.find(jsonStr->valuestring);
    if (entry == PERF_CFG_STR_TO_CHS.end()) {
        return jsonStr->valuestring;
    }
    return entry->second;
}

}  // namespace PERF